#ifndef _NE_CAMERA_H_
#define _NE_CAMERA_H_

#include <mathematics/Vector2.h>
#include <mathematics/Frustum.h>
#include <mathematics/Ray.h>
#include <mathematics/Node.h>

namespace ne
{
    enum ProjectionType
    {
        PT_ORTHOGRAPHIC,
        PT_PERSPECTIVE
    };

    class NEAPI Camera : public Node
    {
        public:
            Camera();

            virtual void setScale(const Vector3 &v);

            void setFOV(const real a);
            real getFOV() const;

            void setAspectRatio(const real a);
            real getAspectRatio() const;

            void setFocalLength(const real a);
            real getFocalLength() const;

            void setNearClipDistance(const real a);
            real getNearClipDistance() const;

            void setFarClipDistance(const real a);
            real getFarClipDistance() const;

            const Matrix4& getViewMatrix() const;
            const Matrix4& getProjectionMatrix() const;

            void setProjectionType(const ProjectionType pt);
            ProjectionType getProjectionType() const;

            const Frustum& getCullingFrustum() const;

            void setFrustumOffset(const Vector2 &v);
            const Vector2& getFrustumOffset() const;

            void setOrthoWindowWidth(const real a);
            real getOrthoWindowWidth() const;
            void setOrthoWindowHeight(const real a);
            real getOrthoWindowHeight() const;

            // Get the World Ray from the Viewport
            // The origin is the Position of World
            Ray getViewportToWorldRay(const real x, const real y) const;
            void getViewportToWorldRay(const real x, const real y, Ray &outRay) const;

            // Get the Camera Ray from the Viewport
            // The origin is the Position of Camera
            Ray getViewportToCameraRay(const real x, const real y) const;
            void getViewportToCameraRay(const real x, const real y, Ray &outRay) const;

            virtual void needUpdate(const bool forceParentUpdate=false);

            virtual ~Camera();

        protected:
            void _invalidateProjection() const;
            void _invalidateFrustumPlanes() const;

            void _updateProjection() const;
            void _updateFrustumPlanes() const;

            void _calcProjectionParameters(real &left, real &right, real &top, real &bottom) const;

        private:
            real mFOV;
            real mRatio;
            real mFocalLength;
            real mNear;
            real mFar;
            ProjectionType mProjectionType;
            mutable Frustum mCullFrustum;
            Vector2 mFrustumOffset;
            real mOrthoHeight;

            mutable Matrix4 mProjectionMatrix;

            mutable bool mDirtyProjection;
            mutable bool mDirtyFrustumPlanes;
    };
}

#endif
